using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using HtmlAgilityPack;
using Windows.Web.Syndication;

namespace FeedReaderApp.Model
{
    public class FeedParser
    {
        private FeedParser()
        {
        }

        private string _feedName;
        private string _feedUrl;
        private SyndicationFeed _syndicationFeed;

        public static FeedParser Initialize(string name, string url)
        {
            var parser = new FeedParser();
            parser._feedName = name;
            parser._feedUrl = url;

            return parser;
        }

        public async Task<bool> LoadAsync()
        {
            var syndicationClient = new SyndicationClient();

            try
            {
                _syndicationFeed = await syndicationClient.RetrieveFeedAsync(new Uri(_feedUrl));
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
                return false;
            }

            return true;
        }

        public List<FeedItem> Parse()
        {
            var items = new List<FeedItem>();

            try
            {
                foreach (SyndicationItem item in _syndicationFeed.Items)
                {
                    string uniqueId = Guid.NewGuid().ToString();

                    string title = item.Title != null ? item.Title.Text : "";
                    string summary = item.Summary != null ? item.Summary.Text : "";
                    string content = item.Content != null ? item.Content.Text : "";
                    string uri = "";

                    // Neues Auslesen der Post Urls 
                    if (item.Links != null && item.Links.Count > 0)
                    {
                        uri = null != item.Links[0].NodeValue && item.Links[0].NodeValue.ToString() != ""
                                  ? item.Links[0].NodeValue
                                  : "#";
                    }
                    else
                    {
                        uri = "#";
                    }

                    if (uri == "#")
                    {
                        uri = null == item.CommentsUri || item.CommentsUri.ToString() == ""
                                  ? (null != item.Id && item.Id.ToString() != "" ? item.Id.ToString() : "#")
                                  : (item.CommentsUri.AbsoluteUri.Replace("#comments", "") ?? "");
                    }

                    if (String.IsNullOrWhiteSpace(content))
                    {
                        content = summary;
                    }

                    if (!String.IsNullOrWhiteSpace(summary))
                    {
                        summary = GetPlainText(summary, 200);
                    }

                    string imagePath = InferImageFromPostContents(item);

                    var cat = new List<string>();
                    if (item.Categories != null && item.Categories.Count > 0)
                    {
                        foreach (var category in item.Categories)
                        {
                            if (!String.IsNullOrWhiteSpace(category.NodeValue))
                                cat.Add(category.NodeValue);
                        }
                    }

                    var feeditem = new FeedItem()
                    {
                        UniqueId = uniqueId,
                        FeedName = _feedName,
                        Summary = summary,
                        Uri = uri,
                        Content = content,
                        Title = title,
                        InferredImageUrl = imagePath,
                        Categories = cat
                    };

                    items.Add(feeditem);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());

                // Absichtlich leer damit nicht halb-volle Items-Listen geliefert werden
                return new List<FeedItem>();
            }

            return items;
        }

        // CW: schaut aus wie http://stackoverflow.com/questions/4935446/string-to-htmldocument-c-sharp
        private string GetPlainText(string summary, int maxLength)
        {
            var htmlDoc = new HtmlAgilityPack.HtmlDocument();
            htmlDoc.LoadHtml(summary);

            var text = htmlDoc.DocumentNode.InnerText.Trim();
            return text.Substring(0, Math.Min(200, text.Length));
        }

        private string InferImageFromPostContents(SyndicationItem item)
        {
            var node = GetImageNode(item);

            if (node == null)
                return null;

            if (0 == String.Compare(node.Name, "img", StringComparison.OrdinalIgnoreCase))
            {
                var srcAttr = node.Attributes["src"];
                if (srcAttr == null)
                    return null;
                return srcAttr.Value;

            }
            if (0 == String.Compare(node.Name, "a", StringComparison.OrdinalIgnoreCase))
            {
                var srcAttr = node.Attributes["href"];
                if (srcAttr == null)
                    return null;
                return srcAttr.Value;
            }

            return null;
        }

        private HtmlNode GetImageNode(SyndicationItem item)
        {
            var htmlDoc = new HtmlDocument();
            htmlDoc.LoadHtml(item.NodeValue);

            var nodes = htmlDoc.DocumentNode.ChildNodes;
            var node = GetFirstTagNode(nodes, ImgTagIgnoreGifsPredicate);
            if (node == null)
            {
                node = GetFirstTagNode(nodes, HrefWithImgTagPredicate);
            }
            return node;
        }

        private bool ImgTagIgnoreGifsPredicate(HtmlNode htmlNode)
        {
            if (htmlNode.Name != "img")
                return false;

            var src = htmlNode.Attributes["src"];

            if (src == null)
                return false;

            var imagePath = src.Value.ToLower();
            var ext = Path.GetExtension(imagePath);
            if (ext == ".gif")
                return false;
            if (imagePath.Contains("pinext.png"))
                return false;
            return true;
        }

        private bool HrefWithImgTagPredicate(HtmlNode htmlNode)
        {
            if (htmlNode.Name != "a")
                return false;
            var href = htmlNode.Attributes["href"];
            if (href == null)
                return false;

            var ext = Path.GetExtension(href.Value).ToLower();
            if (ext == ".jpg" || ext == ".png")
                return true;
            return false;
        }


        private HtmlNode GetFirstTagNode(IEnumerable<HtmlNode> nodes, Predicate<HtmlNode> predicate)
        {
            foreach (var node in nodes)
            {
                if (predicate(node))
                    return node;

                if (node.ChildNodes.Count > 0)
                {
                    var htmlNode = GetFirstTagNode(node.ChildNodes, predicate);
                    if (htmlNode != null)
                        return htmlNode;
                }
            }
            return null;
        }
    }
}
